package com.zenghm.recyclerview.paging;

import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

/**
 * 分页布局，滑动翻页
 *
 * @author zenghm
 */
public class PagingLayoutManager extends RecyclerView.LayoutManager {

    private RecyclerView mRecyclerView;
    @RecyclerView.Orientation
    private final int mOrientation;
    private final int mColumns;
    private final int mRows;

    private int mTotalWidth;
    private int mTotalHeight;
    private int mHorizontalOffest = 0;
    private int mVerticalOffest = 0;

    /**
     * @param orientation 分页方向
     * @param columns     每页列数
     * @param rows        每页行数
     */
    public PagingLayoutManager(@RecyclerView.Orientation int orientation, int columns, int rows) {
        mOrientation = orientation;
        mColumns = columns;
        mRows = rows;
    }

    @Override
    public void onAttachedToWindow(RecyclerView view) {
        super.onAttachedToWindow(view);
        view.setHasFixedSize(true);
        mRecyclerView = view;
    }

    @Override
    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
        super.onDetachedFromWindow(view, recycler);
        mRecyclerView = null;
    }

    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (getItemCount() == 0 || state.isPreLayout())
            return;
        detachAndScrapAttachedViews(recycler);

        int pageSize = mColumns * mRows;
        int pageCount = (getItemCount() - 1) / pageSize + 1;

        int screenWidth = getDisplayWidth();
        int screenHeight = getDisplayHeight();

        int itemWidth = screenWidth / mColumns;
        int itemHeight = screenHeight / mRows;

        for (int position = 0; position < getItemCount(); position++) {
            View scrap = recycler.getViewForPosition(position);
            addView(scrap);

            measureChildWithMargins(scrap, 0, 0);

            RecyclerView.LayoutParams rvlp = (RecyclerView.LayoutParams) scrap.getLayoutParams();
            rvlp.width = itemWidth - getLeftDecorationWidth(scrap) - getRightDecorationWidth(scrap);
            rvlp.height = itemHeight - getTopDecorationHeight(scrap) - getBottomDecorationHeight(scrap);

            measureChildWithMargins(scrap, 0, 0);

            int page = position / pageSize;
            int col = position % mColumns;
            int row = position / mColumns;
            int left;
            int top;
            if (mOrientation == RecyclerView.HORIZONTAL) {
                row = row % mRows;
                left = getPaddingLeft() + page * screenWidth + col * itemWidth;
                top = getPaddingTop() + row * itemHeight;
            } else {
                left = getPaddingLeft() + col * itemWidth;
                top = getPaddingTop() + row * itemHeight;
            }
            layoutDecorated(scrap, left, top, left + itemWidth, top + itemHeight);
        }
        mTotalWidth = pageCount * screenWidth;
        mTotalHeight = pageCount * screenHeight;
        offsetChildrenHorizontal(-mHorizontalOffest);
        offsetChildrenVertical(-mVerticalOffest);
    }

    @Override
    public int computeHorizontalScrollExtent(@NonNull RecyclerView.State state) {
        return getDisplayWidth();
    }

    @Override
    public int computeHorizontalScrollRange(@NonNull RecyclerView.State state) {
        return mTotalWidth;
    }

    @Override
    public int computeHorizontalScrollOffset(@NonNull RecyclerView.State state) {
        return mHorizontalOffest;
    }

    @Override
    public boolean canScrollHorizontally() {
        return mOrientation == RecyclerView.HORIZONTAL;
    }

    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int theVisibleWidth = getDisplayWidth();
        int theMoreWidth = mTotalWidth - theVisibleWidth;
        if (mTotalWidth <= theVisibleWidth) {
            dx = 0;
        } else if (mHorizontalOffest + dx < 0) { //抵达左边界
            dx = -mHorizontalOffest;
        } else if (mHorizontalOffest + dx > theMoreWidth) { //抵达右边界
            dx = theMoreWidth - mHorizontalOffest;
        }
        mHorizontalOffest += dx;
        offsetChildrenHorizontal(-dx);
        return dx;
    }

    @Override
    public int computeVerticalScrollExtent(@NonNull RecyclerView.State state) {
        return getDisplayHeight();
    }

    @Override
    public int computeVerticalScrollRange(@NonNull RecyclerView.State state) {
        return mTotalHeight;
    }

    @Override
    public int computeVerticalScrollOffset(@NonNull RecyclerView.State state) {
        return mVerticalOffest;
    }

    @Override
    public boolean canScrollVertically() {
        return mOrientation == RecyclerView.VERTICAL;
    }

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int theVisibleHeight = getDisplayHeight();
        int theMoreHeight = mTotalHeight - theVisibleHeight;
        if (mTotalHeight <= theVisibleHeight) {
            dy = 0;
        } else if (mVerticalOffest + dy < 0) { //抵达上边界
            dy = -mVerticalOffest;
        } else if (mVerticalOffest + dy > theMoreHeight) { //抵达下边界
            dy = theMoreHeight - mVerticalOffest;
        }
        mVerticalOffest += dy;
        offsetChildrenVertical(-dy);
        return dy;
    }

    @Override
    public void onScrollStateChanged(int state) {
        if (state == RecyclerView.SCROLL_STATE_IDLE) {
            int page;
            if (mOrientation == RecyclerView.HORIZONTAL) {
                page = (int) (mHorizontalOffest * 1f / getDisplayWidth() + 0.5f);
            } else {
                page = (int) (mVerticalOffest * 1f / getDisplayHeight() + 0.5f);
            }
            scrollToPage(page);
        }
    }

    @Override
    public void scrollToPosition(int position) {
        int pageSize = mColumns * mRows;
        int page = position / pageSize;
        scrollToPage(page);
    }

    public void scrollToPage(int page) {
        if (mRecyclerView == null)
            return;
        int dx = 0, dy = 0;
        if (mOrientation == RecyclerView.HORIZONTAL) {
            dx = page * getDisplayWidth() - mHorizontalOffest;
        } else {
            dy = page * getDisplayHeight() - mVerticalOffest;
        }
        mRecyclerView.smoothScrollBy(dx, dy);
    }

    private int getDisplayWidth() {
        return getWidth() - getPaddingLeft() - getPaddingRight();
    }

    private int getDisplayHeight() {
        return getHeight() - getPaddingTop() - getPaddingBottom();
    }
}
